﻿using System;
using Csla;
using System.ComponentModel.DataAnnotations;
using BizControl.Resources;
using BizControl.DalLinq;
using BizControl.Library.BusinessObjects.People;
using BizControl.Library.BusinessObjects.Models;
using Csla.Data;
using System.Linq;
using System.Data.Linq;
using BizControl.Library.Security;

namespace BizControl.Library.BusinessObjects.Marks
{
  [Serializable()]
  public class PersonMarkBO : BusinessBase<PersonMarkBO>, ISupportEmptyValue
  {
    #region Business Methods

    public const string PersonMarkTagName = @"Skill";

    private static readonly PropertyInfo<Guid> MarkIdProperty = RegisterProperty<Guid>(c => c.MarkId);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"MarkIdRequiredPropertyError")]
    public Guid MarkId
    {
      get { return GetProperty(MarkIdProperty); }
      private set { SetProperty(MarkIdProperty, value); }
    }

    private static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(c => c.Name);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"MarkNameRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"MarkNameProperty")]
    public string Name
    {
      get { return GetProperty(NameProperty); }
      set { SetProperty(NameProperty, value); }
    }

    private static readonly PropertyInfo<string> TagProperty = RegisterProperty<string>(c => c.Tag);
    [Display(ResourceType = typeof(PropertiesNames), Name = @"MarkTagProperty")]
    public string Tag
    {
      get { return PersonMarkTagName; }
    }

    private static readonly PropertyInfo<DateTime?> DateSetProperty = RegisterProperty<DateTime?>(c => c.DateSet);
    [Display(ResourceType = typeof(PropertiesNames), Name = @"MarkDateSetProperty")]
    public DateTime? DateSet
    {
      get { return GetProperty(DateSetProperty); }
      private set { SetProperty(DateSetProperty, value); }
    }

    private static readonly PropertyInfo<bool> IsActiveProperty = RegisterProperty<bool>(c => c.IsActive, null, false);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"MarkIsActiveRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"MarkIsActiveProperty")]
    public bool IsActive
    {
      get { return GetProperty(IsActiveProperty); }
      private set { SetProperty(IsActiveProperty, value); }
    }

    private static readonly PropertyInfo<Guid?> ModelProperty = RegisterProperty<Guid?>(c => c.ModelId);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"ModelRequiredError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ModelIdProperty")]
    internal Guid? ModelId
    {
      get { return GetProperty(ModelProperty); }
      set { SetProperty(ModelProperty, value); }
    }

    private static readonly PropertyInfo<bool> HasDetailsProperty = RegisterProperty<bool>(c => c.HasDetails);
    public bool HasDetails
    {
      get { return Details.Count > 0; }
    }

    #region Common properties

    private static readonly PropertyInfo<bool> IsDeletedFieldProperty = RegisterProperty<bool>(c => c.IsDeletedField, null, false);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"IsDeletedRequiredError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"IsDeletedFieldProperty")]
    public bool IsDeletedField
    {
      get { return GetProperty(IsDeletedFieldProperty); }
      private set { SetProperty(IsDeletedFieldProperty, value); }
    }

    private static readonly PropertyInfo<PersonInfo> CreatedByProperty = RegisterProperty<PersonInfo>(c => c.CreatedBy);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"CreatedByRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"CreatedByProperty")]
    public PersonInfo CreatedBy
    {
      get { return GetProperty(CreatedByProperty); }
      private set { SetProperty(CreatedByProperty, value); }
    }

    private static readonly PropertyInfo<DateTime> CreatedOnProperty = RegisterProperty<DateTime>(c => c.CreatedOn);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"CreatedOnRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"CreatedOnProperty")]
    public DateTime CreatedOn
    {
      get { return GetProperty(CreatedOnProperty); }
      private set { SetProperty(CreatedOnProperty, value); }
    }

    private static readonly PropertyInfo<DateTime> LastChangedProperty =
      RegisterProperty<DateTime>(c => c.LastChanged, null, new SmartDate(DateTime.Now, SmartDate.EmptyValue.MaxDate));
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"LastChangedRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"LastChangedProperty")]
    public DateTime LastChanged
    {
      get { return GetProperty(LastChangedProperty); }
      private set { SetProperty(LastChangedProperty, value); }
    }

    private static readonly PropertyInfo<PersonInfo> LastChangedByProperty = RegisterProperty<PersonInfo>(c => c.LastChangedBy);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"LastChangedByRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"LastChangedByProperty")]
    public PersonInfo LastChangedBy
    {
      get { return GetProperty(LastChangedByProperty); }
      private set { SetProperty(LastChangedByProperty, value); }
    }

    private static readonly PropertyInfo<byte[]> TimeStampProperty = RegisterProperty<byte[]>(c => c.TimeStamp);
    private byte[] TimeStamp
    {
      get { return GetProperty(TimeStampProperty); }
      set { SetProperty(TimeStampProperty, value); }
    }

    #endregion

    #region Children

    private static readonly PropertyInfo<MarkPeople> PeopleProperty = RegisterProperty<MarkPeople>(p => p.People, RelationshipTypes.Child | RelationshipTypes.LazyLoad);
    public MarkPeople People
    {
      get
      {
        if (!(FieldManager.FieldExists(PeopleProperty)))
          People = MarkPeople.NewMarkPeople(this);
        return GetProperty(PeopleProperty);
      }
      private set
      {
        LoadProperty(PeopleProperty, value);
        OnPropertyChanged(PeopleProperty);
      }
    }

    private static readonly PropertyInfo<MarkDetails> DetailsProperty = RegisterProperty<MarkDetails>(p => p.Details, RelationshipTypes.Child | RelationshipTypes.LazyLoad);
    public MarkDetails Details
    {
      get
      {
        if (!(FieldManager.FieldExists(DetailsProperty)))
          Details = MarkDetails.NewMarkDetails(new MarkInfo(this));
        return GetProperty(DetailsProperty);
      }
      private set
      {
        LoadProperty(DetailsProperty, value);
        OnPropertyChanged(DetailsProperty);
      }
    }

    #endregion

    internal void LoadData(Mark markDB)
    {
      if (markDB == null) return;
      MarkId = markDB.MarkId;
      Name = markDB.Name;
      DateSet = markDB.DateSet;
      IsActive = markDB.IsActive;
      ModelId = markDB.Model;

      // Common properties
      CreatedBy = new PersonInfo(markDB.CreatedByRef);
      LastChangedBy = new PersonInfo(markDB.LastChangedByRef);

      CreatedOn = markDB.CreatedOn;
      LastChanged = markDB.LastChanged;

      IsDeletedField = markDB.IsDeleted;
      TimeStamp = markDB.TimeStamp.ToArray();
    }

    public override string ToString()
    {
      return MarkId.ToString();
    }

    #endregion

    #region Business Rules

    protected override void AddBusinessRules()
    {
      base.AddBusinessRules();
    }

    #endregion

    #region Authorization rules

    private static void AddObjectAuthorizationRules()
    {
      //BusinessRules.AddRule(typeof(PersonMarkBO), new IsInRole(AuthorizationActions.CreateObject, "PersonMarkBO.Create()"));
      //BusinessRules.AddRule(typeof(PersonMarkBO), new IsInRole(AuthorizationActions.GetObject, "PersonMarkBO.Get()"));
      //BusinessRules.AddRule(typeof(PersonMarkBO), new IsInRole(AuthorizationActions.EditObject, "PersonMarkBO.Edit()"));
      //BusinessRules.AddRule(typeof(PersonMarkBO), new IsInRole(AuthorizationActions.DeleteObject, "PersonMarkBO.Delete()"));
    }

    #endregion

    #region Data Access

    //[RunLocal]
    protected void DataPortal_Create(ModelBO model)
    {
      if (model == null) throw new ArgumentNullException(ExceptionsMessages.ModelNullReference);
      var currentUser = SecurityChecks.GetCurrentUser();
      using (BypassPropertyChecks)
      {
        MarkId = Guid.NewGuid();
        ModelId = model.ModelId;
        CreatedBy = new PersonInfo(currentUser);
        LastChangedBy = new PersonInfo(currentUser);
        CreatedOn = DateTime.Now;
      }
      base.DataPortal_Create();
    }

    private void DataPortal_Fetch(SingleCriteria<PersonMarkBO, Guid> id)
    {
      using (var ctx = ContextManager<BusinessControl2DataContext>.GetManager(Database.BizControlDB))
      {
        var data = (from m in ctx.DataContext.Marks
                    where m.MarkId == id.Value && m.Tag == PersonMarkTagName && !m.IsDeleted
                    select m).SingleOrDefault();
        using (BypassPropertyChecks)
        {
          this.LoadData(data);
        }
        if (data != null)
        {
          Details = MarkDetails.GetMarkDetails(data.MarkDetails.ToArray(), new MarkInfo(this));
          
          var currentModel = SecurityChecks.GetCurrentModel();
          var filteredData = (from p in data.MarkPeople
                              from m in ctx.DataContext.People
                              from pm in ctx.DataContext.PersonModels
                              where p.Person == m.PersonId && !p.IsDeleted && !m.IsDeleted && pm.ModelId == currentModel.ModelId 
                              && pm.PersonId == m.PersonId
                              select p);
          People = MarkPeople.GetMarkPeople(filteredData.ToArray(), this);
        }
      }
    }

    [Transactional(TransactionalTypes.TransactionScope)]
    protected override void DataPortal_Insert()
    {
      using (var ctx = ContextManager<BusinessControl2DataContext>.GetManager(Database.BizControlDB))
      {
        Binary lastTimeStamp = null;
        using (BypassPropertyChecks)
        {
          LastChanged = DateTime.Now;
          ctx.DataContext.AddMark(
            MarkId,
            default(Guid?),
            default(Guid?),
            default(Guid?),
            ModelId,
            Name,
            Tag,
            default(DateTime?),
            default(int),
            IsActive,
            DateSet,
            CreatedBy == null || CreatedBy.IsEmpty() ? (Guid?)null : CreatedBy.Id,
            IsDeletedField,
            CreatedOn,
            LastChanged,
            LastChangedBy == null || LastChangedBy.IsEmpty() ? (Guid?)null : LastChangedBy.Id,
            ref lastTimeStamp);
          TimeStamp = lastTimeStamp.ToArray();
        }
        // update child objects
        //FieldManager.UpdateChildren(this);
        DataPortal.UpdateChild(People, this);
        DataPortal.UpdateChild(Details, this);
      }
    }

    [Transactional(TransactionalTypes.TransactionScope)]
    protected override void DataPortal_Update()
    {
      using (var ctx = ContextManager<BusinessControl2DataContext>.GetManager(Database.BizControlDB))
      {
        if (IsSelfDirty)
        {
          Binary lastTimeStamp = null;
          LastChangedBy = new PersonInfo(SecurityChecks.GetCurrentUser());
          LastChanged = DateTime.Now;
          using (BypassPropertyChecks)
          {
            ctx.DataContext.UpdateMark(
              MarkId,
              default(Guid?),
              default(Guid?),
              default(Guid?),
              ModelId,
              Name,
              Tag,
              default(DateTime?),
              default(int),
              IsActive,
              DateSet,
              IsDeletedField,
              LastChanged,
              LastChangedBy == null || LastChangedBy.IsEmpty() ? (Guid?)null : LastChangedBy.Id,
              TimeStamp,
              ref lastTimeStamp);
            TimeStamp = lastTimeStamp.ToArray();
          }
        }
        // update child objects
        FieldManager.UpdateChildren(this);
      }
    }

    [Transactional(TransactionalTypes.TransactionScope)]
    protected override void DataPortal_DeleteSelf()
    {
      DataPortal_Delete(new SingleCriteria<PersonMarkBO, Guid>(MarkId));
    }

    [Transactional(TransactionalTypes.TransactionScope)]
    private void DataPortal_Delete(SingleCriteria<PersonMarkBO, Guid> criteria)
    {
      IsDeletedField = true;
      DataPortal_Update();
    }

    #endregion

    #region Factory Methods

    private PersonMarkBO()
    { /* require use of factory methods */ }

    public static PersonMarkBO NewPersonMark(ModelBO model)
    {
      return Csla.DataPortal.Create<PersonMarkBO>(model);
    }

    public static PersonMarkBO GetPersonMark(Guid id)
    {
      return Csla.DataPortal.Fetch<PersonMarkBO>(new SingleCriteria<PersonMarkBO, Guid>(id));
    }

    public static PersonMarkBO NewPersonMark()
    {
      var model = (ApplicationContext.User as BizControlPrincipal).Model;
      return Csla.DataPortal.Create<PersonMarkBO>(model);
    }

    #endregion

    #region ISupportEmtpyValue Members

    public bool IsEmpty()
    {
      return MarkId == Guid.Empty;
    }

    #endregion
  }
}
